/*
 * @文件名称： HttpEnginer.java 
 * @文件描述：
 * @版权所有: (C)2008-2012 
 * @创建日期: 2012-8-3  
 * @作者：           lizhen_a  
 */
package com.yidin.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Date;
import java.text.SimpleDateFormat;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.util.EntityUtils;
import android.util.Base64;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.StatFs;
import android.telephony.TelephonyManager;
import android.webkit.WebSettings;
import android.webkit.WebView;

public class HttpEnginer {

	private static final int REQUEST_TIMEOUT = 30 * 1000;// 设置请求超时30秒钟
	private static final int SO_TIMEOUT = 30 * 1000; // 设置等待数据超时时间30秒钟
	private static final String X_SIGNATURE_KEY = "1234567";

	private Context mContext = null;
	private HttpEnginerNotify mNotify = null;

	private boolean mCanceled = false;

	public HttpEnginer(Context context, HttpEnginerNotify notify) {

		mContext = context;
		mNotify = notify;
	}

	public interface HttpEnginerNotify {
		public void progressNotify(int currentSize, int totalSize);
	}

	public String doHttpPost(String url, String postStr) {
		HttpClient httpClient = new DefaultHttpClient();

		if (!isNetworkAvailable(httpClient)) {
			return null;
		}

		ALog.__Log__("doHttpPost url=");
		ALog.__HEX__(url);

		HttpPost post = new HttpPost(url);
		HttpEntity entity = null;

		try {
			entity = new StringEntity(postStr);
		} catch (UnsupportedEncodingException e1) {
			e1.printStackTrace();
			ALog.__Log__("UnsupportedEncodingException" + e1.getMessage());
		}
		post.setEntity(entity);
		post.setHeaders(this.setHttpHeader(url));

		ALog.__Log__("doHttpPost postStr=");
		ALog.__HEX__(postStr);

		setHttpProperties(httpClient);

		String retStr = null;
		try {
			HttpResponse response = httpClient.execute(post);

			int statusCode = response.getStatusLine().getStatusCode();
			String reason = response.getStatusLine().getReasonPhrase();

			ALog.__Log__(String.format("%s = %d,%s = %s",
					"HttpPost resp statusCode", statusCode, "ReasonPhrase",
					reason));

			if (statusCode == HttpStatus.SC_OK) {
				retStr = EntityUtils.toString(response.getEntity(), "utf-8");
				ALog.__HEX__(retStr);

			}

		} catch (Exception e) {
			ALog.__Log__("HttpPost network failed,e.getMessage="
					+ e.getMessage());
		} finally {
			httpClient.getConnectionManager().shutdown();
		}

		return retStr;

	}

	private Bundle isPartialContent(String url) {

		Bundle bundle = new Bundle();
		bundle.putBoolean("partial", false);
		bundle.putInt("contentlen", -1);

		HttpClient httpClient = new DefaultHttpClient();
		setHttpProperties(httpClient);

		HttpGet httpGet = new HttpGet(url);
		httpGet.setHeaders(this.setHttpHeader(url));
		httpGet.addHeader("Range", "bytes=0-");

		try {
			HttpResponse response = httpClient.execute(httpGet);
			int statusCode = response.getStatusLine().getStatusCode();

			if (statusCode == HttpStatus.SC_PARTIAL_CONTENT) {
				bundle.putBoolean("partial", true);

				HttpEntity entity = response.getEntity();

				if (null != entity) {
					bundle.putInt("contentlen", (int) entity.getContentLength());
				}
			}
		} catch (IOException e) {

		} finally {
			httpClient.getConnectionManager().shutdown();
		}

		ALog.__Log__(String.format("%s = %b,content len = %d",
				"Download Server support partial content",
				bundle.getBoolean("partial"), bundle.getInt("contentlen")));

		return bundle;
	}

	private int existFileLen(String filePath, String fileName) {
		int ret = 0;

		File path = new File(filePath);
		if (!path.exists()) {
			path.mkdir();
		} else {
			try {
				FileInputStream fs = new FileInputStream(new File(filePath
						+ fileName));
				ret = fs.available();

			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		ALog.__Log__(String
				.format("%s,file len = %d", filePath + fileName, ret));
		return ret;
	}

	private String getWithPartial(String url, String filePath, String fileName,
			Bundle bundle) {
		String ret = null;

		HttpClient httpClient = new DefaultHttpClient();
		setHttpProperties(httpClient);

		HttpGet httpGet = new HttpGet(url);
		// httpGet.setHeaders(this.setHttpHeader(url));

		int len = bundle.getInt("contentlen");
		int existLen = existFileLen(filePath, fileName);

		if (bundle.getBoolean("partial") && (existLen < len)) {
			String b = String.format("%s%d-", "bytes=", existLen);
			ALog.__Log__(String.format("%s,Range:%s", "addHeader", b));

			httpGet.addHeader("Range", b);
		} else {
			ALog.__Log__("can NOT support partial connect, delete exist file");

			File f = new File(filePath + fileName);
			f.delete();
			existLen = 0;
		}

		try {
			HttpResponse response = httpClient.execute(httpGet);
			int statusCode = response.getStatusLine().getStatusCode();
			String reason = response.getStatusLine().getReasonPhrase();

			ALog.__Log__(String.format("%s = %d,%s = %s",
					"getWithPartial resp statusCode", statusCode,
					"ReasonPhrase", reason));

			if (HttpStatus.SC_PARTIAL_CONTENT == statusCode
					|| HttpStatus.SC_OK == statusCode) {
				HttpEntity entity = response.getEntity();

				if (null != entity) {
					ALog.__Log__("http response headers:");
					Header[] headers = response.getAllHeaders();
					for (int i = 0; i < headers.length; i++) {
						ALog.__Log__(headers[i].getName() + ":"
								+ headers[i].getValue());
					}

					// server can NOT support partial content ,
					// getContentLength() = -1,
					// we should get length from http headers , without 'Rang'
					// in request

					Header[] lengthHeaders = response
							.getHeaders("Content-Length");
					int acceptLen = 0;
					if (lengthHeaders.length > 0) {
						acceptLen = Integer.parseInt(lengthHeaders[0]
								.getValue());
					}

					if (isEnoughRoom(acceptLen)) {
						FileOutputStream out = new FileOutputStream(filePath
								+ fileName, true);
						InputStream input = entity.getContent();

						byte b[] = new byte[1024];
						int j = 0;
						int count = existLen;

						while ((j = input.read(b)) != -1) {
							out.write(b, 0, j);
							count += j;
							mNotify.progressNotify(count, acceptLen);

							if (mCanceled) {
								httpGet.abort();
								ret = "cancel download";
								// break;

							}
						}

						out.flush();
						out.close();
						input.close();

						ALog.__Log__("getWithPartial Successful");
					} else {
						ret = "SDCard Room is NOT enough";
					}

				} else {
					ret = "null == entity";
				}

			} else {
				ret = String.format("%s = %d,%s = %s",
						"statusCode error,statusCode", statusCode,
						"ReasonPhrase", reason);
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

			ret = "getWithPartial error";
		} finally {
			httpClient.getConnectionManager().shutdown();
		}

		return ret;
	}

	public String doHttpGet(String url, String filePath, String fileName) {
		String err = null;

		if (0 == url.length()) {
			return "url为空值";
		}

		HttpClient httpClient = new DefaultHttpClient();

		if (!isNetworkAvailable(httpClient)) {
			return "网络不可用";
		}

		if (!isSDCardExist()) {
			return "SD卡不存在";
		}

		ALog.__Log__("doHttpGet url = ");
		ALog.__HEX__(url);

		mCanceled = false;

		Bundle bundle = isPartialContent(url);

		err = getWithPartial(url, filePath, fileName, bundle);

		ALog.__Log__("doHttpGet err = " + err);

		return err;
	}

	public String doHttpGetData(String url) {
		String ret = null;

		if (0 == url.length()) {
			return ret;
		}

		HttpClient httpClient = new DefaultHttpClient();

		if (!isNetworkAvailable(httpClient)) {
			ALog.__Log__("网络不可用");
			return ret;
		}

		ALog.__Log__("doHttpGetData url=");
		ALog.__HEX__(url);

		HttpGet httpGet = new HttpGet(url);
		setHttpProperties(httpClient);
		httpGet.setHeaders(this.setHttpHeader(url));

		try {
			HttpResponse response = httpClient.execute(httpGet);
			int statusCode = response.getStatusLine().getStatusCode();
			String reason = response.getStatusLine().getReasonPhrase();

			ALog.__Log__(String.format("%s = %d,%s = %s",
					"doHttpGetData resp statusCode", statusCode,
					"ReasonPhrase", reason));

			if (statusCode == HttpStatus.SC_OK) {
				HttpEntity entity = response.getEntity();

				if (entity != null) {
					ALog.__Log__("http response headers:");

					Header[] headers = response.getAllHeaders();
					for (int i = 0; i < headers.length; i++) {
						ALog.__Log__(headers[i].getName() + ":"
								+ headers[i].getValue());
					}

					int len = 10;
					// server can NOT support partial content ,
					// getContentLength() = -1,
					Header[] lengthHeaders = response
							.getHeaders("Content-Length");
					if (lengthHeaders.length > 0) {
						len = Integer.parseInt(lengthHeaders[0].getValue());
					}

					StringBuffer buf = new StringBuffer(len);
					byte b[] = new byte[1024];

					InputStream input = entity.getContent();
					while ((input.read(b)) != -1) {
						buf.append(b);
					}

					ret = buf.toString();

					ALog.__Log__("Get Data Successful");
				}
			}

		} catch (IOException e) {
			ALog.__Log__("HttpGetData network failed,e.getMessage="
					+ e.getMessage());

		} finally {
			httpClient.getConnectionManager().shutdown();
		}

		return ret;
	}

	public void doHttpGetCancel() {
		mCanceled = true;
	}

	private void setHttpProperties(HttpClient client) {
		// 请求超时
		client.getParams().setParameter(
				CoreConnectionPNames.CONNECTION_TIMEOUT, REQUEST_TIMEOUT);
		// 读取超时
		client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT,
				SO_TIMEOUT);

		ConnectivityManager manager = (ConnectivityManager) mContext
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo networkinfo = manager.getActiveNetworkInfo();
		String net = networkinfo.getExtraInfo();// 获取网络类型
		if (net != null)// wifi的值为空，这里判断下
		{
			ALog.__Log__("ActiveNet = " + net);

			if (net.equals("cmwap") || net.equals("uniwap")) {
				HttpHost proxy = new HttpHost("10.0.0.172", 80);
				client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
						proxy);
			}
			if (net.equals("ctwap") || net.equals("ctnet")) // 没用过电信的,网上查到电信net,wap都要代理,有点奇怪
			{
				HttpHost proxy = new HttpHost("10.0.0.200", 80);
				client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
						proxy);
			}
		} else {
			ALog.__Log__("Active Network = WiFi");
		}
	}

	private boolean isNetworkAvailable(HttpClient client) {
		boolean ret = true;
		ConnectivityManager manager = (ConnectivityManager) mContext
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo networkinfo = manager.getActiveNetworkInfo();
		if (null == networkinfo) {
			ret = false;
		}
		ALog.__Log__(String.format("%s = %b", "isNetworkAvailable", ret));
		return ret;
	}

	private boolean isSDCardExist() {
		boolean ret = false;

		if (android.os.Environment.getExternalStorageState().equals(
				android.os.Environment.MEDIA_MOUNTED)) {
			ret = true;
		}

		ALog.__Log__(String.format("%s = %b", "isSDCardExist", ret));
		return ret;
	}

	private boolean isEnoughRoom(int reqLen) {
		boolean ret = false;

		File path = android.os.Environment.getExternalStorageDirectory();
		StatFs statfs = new StatFs(path.getPath());

		int blockSize = statfs.getBlockSize();
		int availaBlock = statfs.getAvailableBlocks();

		int quest = reqLen /blockSize;
		//double sum = blockSize * availaBlock;

		if (availaBlock/blockSize > quest) {
			ret = true;
		}

		ALog.__Log__(String.format("%s = %d,%s = %d,isEnoughRoom = %b",
				"sum size", availaBlock/blockSize, "reqLen", reqLen, ret));

		return ret;
	}

	private String getIMSI() {
		String imsi = null;

		TelephonyManager telManager = (TelephonyManager) mContext
				.getSystemService(Context.TELEPHONY_SERVICE);
		imsi = telManager.getSubscriberId();

		return imsi;
	}

	private String getUA() {
		String ua = null;

		WebView webview;

		webview = new WebView(mContext);

		webview.layout(0, 0, 0, 0);

		WebSettings settings = webview.getSettings();

		ua = settings.getUserAgentString();

		return ua;
	}

	private String getTimeStamp() {
		String ret = null;

		Date now = new Date(System.currentTimeMillis());
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		ret = sdf.format(now);

		return ret;
	}

	private String md5(String string) {
		byte[] hash;
		try {
			hash = MessageDigest.getInstance("MD5").digest(
					string.getBytes("UTF-8"));
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException("Huh, MD5 should be supported?", e);
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("Huh, UTF-8 should be supported?", e);
		}

		StringBuilder hex = new StringBuilder(hash.length * 2);
		for (byte b : hash) {
			if ((b & 0xFF) < 0x10)
				hex.append("0");
			hex.append(Integer.toHexString(b & 0xFF));
		}
		return hex.toString().toUpperCase();
	}

	private Header[] setHttpHeader(String url) {
		Header[] headers = new Header[8];

		BasicHeader basicHeader = null;

		// X-USER-ID
		String userID = (Base64.encodeToString(getIMSI().getBytes(),
				Base64.DEFAULT)).trim();
		// basicHeader = new BasicHeader("X-USER-ID",userID);
		basicHeader = new BasicHeader("appname", "MM3.1.0.001.01_CTAndroid_JT");
		headers[0] = basicHeader;

		// User-Agent
		basicHeader = new BasicHeader("User-Agent","");//getUA()
		headers[1] = basicHeader;

		// X-Channel-Code
		basicHeader = new BasicHeader("X-Channel-Code", "");
		headers[2] = basicHeader;

		// X-Timestamp
		String stamp = getTimeStamp();
		basicHeader = new BasicHeader("X-Timestamp", stamp);
		headers[3] = basicHeader;

		// X-Signature
		basicHeader = new BasicHeader("X-Signature", md5(url + userID + stamp
				+ X_SIGNATURE_KEY));
		headers[4] = basicHeader;

		// Accept
		basicHeader = new BasicHeader("Accept",
				"text/xml,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
		headers[5] = basicHeader;

		// Accept-Language
		basicHeader = new BasicHeader("Accept-Language",
				"zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3");
		headers[6] = basicHeader;

		// Accept-Encoding
		basicHeader = new BasicHeader("Accept-Encoding", "gzip,.deflate");
		headers[7] = basicHeader;

		int i = 0;
		for (Header h : headers) {
			ALog.__Log__(String.format("headers[%d],%s,%s", i, h.getName(),
					h.getValue()));
			++i;
		}

		return headers;
	}

}
